package com.xiaohua.api.controller;

import com.xiaohua.common.core.domain.AjaxResult;
import com.xiaohua.common.core.controller.BaseController;
import com.xiaohua.common.annotation.Log;
import com.xiaohua.common.enums.BusinessType;
import com.xiaohua.common.utils.StringUtils;
import com.xiaohua.common.utils.file.FileUtils;
import com.xiaohua.common.utils.ServletUtils;
import com.xiaohua.api.service.IXhCertService;
import com.xiaohua.common.service.ITokenService;
import com.xiaohua.common.core.domain.model.LoginUser;
import com.xiaohua.api.service.IOperationLogService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.beans.factory.annotation.Qualifier;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.http.MediaType;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Base64;
import java.util.Map;
import java.util.HashMap;

/**
 * 证件识别控制器
 *
 * @author 小花
 * @date 2025-03-29
 */
@Api(tags = "证件识别接口")
@RestController
@RequestMapping("/api/cert")
public class XhCertController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(XhCertController.class);

    @Autowired
    private IXhCertService xhCertService;

    @Autowired
    @Qualifier("xhApiTokenServiceImpl")
    private ITokenService tokenService;
    
    @Autowired
    private IOperationLogService operationLogService;

    /**
     * API功能接口，用于封装API调用逻辑
     */
    @FunctionalInterface
    private interface APIFunction {
        /**
         * 执行API调用
         *
         * @return API调用结果
         */
        AjaxResult execute() throws Exception;
    }

    /**
     * 验证用户token并在成功调用API后扣减token
     *
     * @param operationType 操作类型，用于日志记录
     * @param apiMethod 要执行的API方法
     * @return API执行结果
     */
    private AjaxResult validateTokenAndExecute(String operationType, APIFunction apiMethod) {
        // 获取当前登录用户
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            log.warn("API调用失败：用户未登录");
            return AjaxResult.error("用户未登录，请先登录");
        }

        Long userId = loginUser.getUserId();
        String username = loginUser.getUsername();
        String requestURI = ServletUtils.getRequest().getRequestURI();
        log.info("用户[{}({})]请求API[{}]调用", username, userId, requestURI);

        // 验证用户是否有足够的tokens
        if (!tokenService.checkUserTokens(userId)) {
            log.warn("用户[{}({})]tokens不足，API[{}]调用被拒绝", username, userId, requestURI);
            // 记录操作日志 - 因tokens不足被拒绝
            operationLogService.logOperation(userId, operationType, 403);
            return AjaxResult.error("您的tokens已用完，请充值后再使用");
        }

        // 性能监控 - 记录开始时间
        long startTime = System.currentTimeMillis();

        // 执行API调用
        AjaxResult result;
        try {
            // 执行实际API调用
            result = apiMethod.execute();

            // 如果API调用成功，扣减用户tokens
            if (result.isSuccess()) {
                boolean deducted = tokenService.deductUserTokens(userId);
                if (deducted) {
                    log.info("用户[{}({})]API[{}]调用成功，tokens已扣减，耗时{}ms",
                            username, userId, requestURI, (System.currentTimeMillis() - startTime));
                    // 记录操作日志 - 成功
                    operationLogService.logOperationFromResult(userId, result, operationType, false);
                } else {
                    // 记录严重错误：API调用成功但token扣减失败
                    log.error("用户[{}({})]API[{}]调用成功，但tokens扣减失败，耗时{}ms",
                            username, userId, requestURI, (System.currentTimeMillis() - startTime));
                    // 记录操作日志 - 成功但token扣减失败
                    operationLogService.logOperationFromResult(userId, result, operationType + "_但TOKEN扣减失败", false);
                }
            } else {
                // API调用失败，不扣减tokens
                log.info("用户[{}({})]API[{}]调用失败(业务错误)，未扣减tokens，耗时{}ms",
                        username, userId, requestURI, (System.currentTimeMillis() - startTime));
                // 记录操作日志 - 业务失败
                operationLogService.logOperation(userId, operationType, 400);
            }
        } catch (Exception e) {
            // 系统异常处理
            long duration = System.currentTimeMillis() - startTime;
            log.error("用户[{}({})]API[{}]调用异常，耗时{}ms, 错误: {}",
                    username, userId, requestURI, duration, e.getMessage(), e);
            // 记录操作日志 - 系统异常
            operationLogService.logOperation(userId, operationType, 500);
            return AjaxResult.error("API调用失败: " + e.getMessage());
        }

        return result;
    }

    /**
     * 处理不同类型的输入参数并执行相应的API方法
     *
     * @param file            文件参数
     * @param fileBase64      Base64编码参数
     * @param imageUrl        URL参数
     * @param base64Processor Base64处理器，处理Base64字符串
     * @param urlProcessor    URL处理器，处理URL字符串
     * @return API执行结果
     * @throws Exception 如果处理过程中出现错误
     */
    private AjaxResult processImageInput(
            MultipartFile file,
            String fileBase64,
            String imageUrl,
            ThrowingFunction<String, AjaxResult> base64Processor,
            ThrowingFunction<String, AjaxResult> urlProcessor) throws Exception {

        if (file != null) {
            byte[] fileBytes = file.getBytes();
            String base64 = Base64.getEncoder().encodeToString(fileBytes);
            return base64Processor.apply(base64);
        } else if (!StringUtils.isEmpty(fileBase64)) {
            return base64Processor.apply(fileBase64);
        } else {
            return urlProcessor.apply(imageUrl);
        }
    }

    /**
     * 处理特殊情况：结婚证等需要直接处理字节数组的识别
     */
    private AjaxResult processImageInputWithBytes(
            MultipartFile file,
            String fileBase64,
            String imageUrl,
            ThrowingFunction<byte[], AjaxResult> fileProcessor,
            ThrowingFunction<String, AjaxResult> base64Processor,
            ThrowingFunction<String, AjaxResult> urlProcessor) throws Exception {

        if (file != null) {
            byte[] fileBytes = file.getBytes();
            return fileProcessor.apply(fileBytes);
        } else if (!StringUtils.isEmpty(fileBase64)) {
            return base64Processor.apply(fileBase64);
        } else {
            return urlProcessor.apply(imageUrl);
        }
    }

    /**
     * 用于处理可能抛出异常的函数接口
     */
    @FunctionalInterface
    private interface ThrowingFunction<T, R> {
        R apply(T t) throws Exception;
    }

    /**
     * 记录异常的详细信息
     *
     * @param apiName    API名称
     * @param file       文件参数
     * @param fileBase64 Base64参数
     * @param imageUrl   URL参数
     * @param e          异常
     */
    private void logDetailedError(String apiName, MultipartFile file, String fileBase64, String imageUrl, Exception e) {
        String errorSource = file != null ? "文件" : (StringUtils.isNotEmpty(fileBase64) ? "Base64" : "URL");
        String sourceDetail = file != null ? file.getOriginalFilename() :
                (StringUtils.isNotEmpty(fileBase64) ? "Base64数据(长度:" + fileBase64.length() + ")" :
                        "URL:" + imageUrl);
        log.error("{}识别失败，来源类型:{}，来源:{}, 错误:{}", apiName, errorSource, sourceDetail, e.getMessage(), e);
    }

    /**
     * 证件分类识别
     */
    @ApiOperation(value = "证件分类识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "证件图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "证件图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "证件图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @PostMapping(value = "/classify", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult classify(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        return validateTokenAndExecute("证件分类识别", () -> {
            try {
                return processImageInput(file, fileBase64, imageUrl,
                        xhCertService::classifyCertificate,
                        xhCertService::classifyCertificateByUrl);
            } catch (Exception e) {
                logDetailedError("证件分类", file, fileBase64, imageUrl, e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 身份证证件识别
     */
    @ApiOperation(value = "身份证识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "身份证图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "身份证图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "身份证图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "身份证识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/id-card", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult idCard(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        return validateTokenAndExecute("身份证识别", () -> {
            try {
                return processImageInput(file, fileBase64, imageUrl,
                        xhCertService::recognizeIdCard,
                        xhCertService::recognizeIdCardByUrl);
            } catch (Exception e) {
                logDetailedError("身份证", file, fileBase64, imageUrl, e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 营业执照识别
     */
    @ApiOperation(value = "营业执照识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "营业执照图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "营业执照图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "营业执照图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "营业执照识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/business-license", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult businessLicense(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("营业执照识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeBusinessLicense,
                        xhCertService::recognizeBusinessLicenseByUrl);
            } catch (Exception e) {
                logDetailedError("营业执照", finalFile, finalFileBase64, finalImageUrl, e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 行驶证识别
     */
    @ApiOperation(value = "行驶证识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "行驶证图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "行驶证图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "行驶证图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "行驶证识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/vehicle-license", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult vehicleLicense(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("行驶证识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeVehicleLicense,
                        xhCertService::recognizeVehicleLicenseByUrl);
            } catch (Exception e) {
                logDetailedError("行驶证", finalFile, finalFileBase64, finalImageUrl, e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 驾驶证识别
     */
    @ApiOperation(value = "驾驶证识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "驾驶证图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "驾驶证图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "驾驶证图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "驾驶证识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/driver-license", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult driverLicense(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("驾驶证识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeDriverLicense,
                        xhCertService::recognizeDriverLicenseByUrl);
            } catch (Exception e) {
                logDetailedError("驾驶证", finalFile, finalFileBase64, finalImageUrl, e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 银行卡识别
     */
    @ApiOperation(value = "银行卡识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "银行卡图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "银行卡图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "银行卡图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "银行卡识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/bank-card", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult bankCard(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("银行卡识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeBankCard,
                        xhCertService::recognizeBankCardByUrl);
            } catch (Exception e) {
                log.error("银行卡识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 护照识别
     */
    @ApiOperation(value = "护照识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "护照图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "护照图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "护照图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "护照识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/passport", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult passport(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("护照识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizePassport,
                        xhCertService::recognizePassportByUrl);
            } catch (Exception e) {
                log.error("护照识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 社保卡识别
     */
    @ApiOperation(value = "社保卡识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "社保卡图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "社保卡图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "社保卡图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "社保卡识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/social-security-card", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult socialSecurityCard(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("社保卡识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeSocialSecurityCard,
                        xhCertService::recognizeSocialSecurityCardByUrl);
            } catch (Exception e) {
                log.error("社保卡识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 户口本识别
     */
    @ApiOperation(value = "户口本识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "户口本图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "户口本图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "户口本图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "户口本识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/household-register", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult householdRegister(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("户口本识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeHouseholdRegister,
                        xhCertService::recognizeHouseholdRegisterByUrl);
            } catch (Exception e) {
                log.error("户口本识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 结婚证识别
     */
    @ApiOperation(value = "结婚证识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "结婚证图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "结婚证图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "结婚证图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "结婚证识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/marriage-certificate", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult marriageCertificate(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("结婚证识别", () -> {
            try {
                return processImageInputWithBytes(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeMarriageCertificateWithBytes,
                        xhCertService::recognizeMarriageCertificate,
                        xhCertService::recognizeMarriageCertificateByUrl);
            } catch (Exception e) {
                log.error("结婚证识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 房产证识别
     */
    @ApiOperation(value = "房产证识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "房产证图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "房产证图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "房产证图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "房产证识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/property-certificate", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult propertyCertificate(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("房产证识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizePropertyCertificate,
                        xhCertService::recognizePropertyCertificateByUrl);
            } catch (Exception e) {
                log.error("房产证识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 出生证明识别
     */
    @ApiOperation(value = "出生证明识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "出生证明图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "出生证明图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "出生证明图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "出生证明识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/birth-certificate", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult birthCertificate(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("出生证明识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeBirthCertificate,
                        xhCertService::recognizeBirthCertificateByUrl);
            } catch (Exception e) {
                log.error("出生证明识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 学位证识别
     */
    @ApiOperation(value = "学位证识别", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "学位证图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "学位证图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "学位证图片的URL地址", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "学位证识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/degree-certificate", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult degreeCertificate(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("学位证识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeDegreeCertificate,
                        xhCertService::recognizeDegreeCertificateByUrl);
            } catch (Exception e) {
                log.error("学位证识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 毕业证识别
     */
    @ApiOperation("毕业证识别")
    @PostMapping("/recognize/graduation_certificate")
    public AjaxResult recognizeGraduationCertificate(
            @RequestParam(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl) {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("毕业证识别", () -> {
            try {
                return processImageInput(finalFile, finalFileBase64, finalImageUrl,
                        xhCertService::recognizeGraduationCertificate,
                        xhCertService::recognizeGraduationCertificateByUrl);
            } catch (Exception e) {
                log.error("毕业证识别失败", e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 统一证件识别接口
     */
    @ApiOperation(value = "统一证件识别接口", notes = "支持三种方式提交图片：1. 图片文件上传；2. Base64编码；3. 图片URL")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "证件图片文件", dataTypeClass = MultipartFile.class, paramType = "form"),
            @ApiImplicitParam(name = "fileBase64", value = "证件图片的Base64编码", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "imageUrl", value = "证件图片的URL地址", dataTypeClass = String.class, paramType = "form"),
            @ApiImplicitParam(name = "certType", value = "证件类型", dataTypeClass = String.class, paramType = "form")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "识别成功")
    })
    @Log(title = "统一证件识别", businessType = BusinessType.OTHER)
    @PostMapping(value = "/recognize", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult recognize(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "imageUrl", required = false) String imageUrl,
            @RequestParam(value = "certType", required = false) String certType) throws IOException {
        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(imageUrl)) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 如果未指定证件类型，则默认为身份证
        final String finalCertType = StringUtils.isEmpty(certType) ? "id_card" : certType;

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalImageUrl = imageUrl;

        return validateTokenAndExecute("统一证件识别", () -> {
            try {
                // 根据不同参数类型调用相应的识别方法
                if (finalFile != null) {
                    byte[] fileBytes = finalFile.getBytes();
                    String base64 = Base64.getEncoder().encodeToString(fileBytes);
                    return xhCertService.recognizeCertificate(base64, finalCertType);
                } else if (!StringUtils.isEmpty(finalFileBase64)) {
                    return xhCertService.recognizeCertificate(finalFileBase64, finalCertType);
                } else {
                    return xhCertService.recognizeCertificateByUrl(finalImageUrl, finalCertType);
                }
            } catch (Exception e) {
                logDetailedError("统一证件(" + finalCertType + ")", finalFile, finalFileBase64, finalImageUrl, e);
                return AjaxResult.error("识别失败: " + e.getMessage());
            }
        });
    }

    /**
     * 证件识别通用接口
     *
     * @param imageBase64 图片的base64编码数据
     * @param certType    证件类型，可选值：id_card, bank_card, business_license等
     * @return 识别结果
     * <p>
     * 注意：
     * 1. 如果响应数据量过大，在页面显示可能会被截断，请使用开发者工具查看完整响应
     * 2. 包含图片数据的字段会被自动截断以优化传输
     * 3. 调用此接口将消耗1个token
     */
    @PostMapping("/recognize")
    public AjaxResult recognize(@RequestParam("imageBase64") String imageBase64,
                                @RequestParam(value = "certType", required = false) String certType) {
        final String finalCertType = StringUtils.isEmpty(certType) ? "id_card" : certType;

        return validateTokenAndExecute("证件识别通用接口", () -> xhCertService.recognizeCertificate(imageBase64, finalCertType));
    }

    /**
     * 根据URL进行证件识别
     *
     * @param imageUrl 图片URL
     * @param certType 证件类型，可选值：id_card, bank_card, business_license等
     * @return 识别结果
     * <p>
     * 注意：
     * 1. 如果响应数据量过大，在页面显示可能会被截断，请使用开发者工具查看完整响应
     * 2. 包含图片数据的字段会被自动截断以优化传输
     * 3. 调用此接口将消耗1个token
     */
    @PostMapping("/recognizeByUrl")
    public AjaxResult recognizeByUrl(@RequestParam("imageUrl") String imageUrl,
                                     @RequestParam(value = "certType", required = false) String certType) {
        final String finalCertType = StringUtils.isEmpty(certType) ? "id_card" : certType;

        return validateTokenAndExecute("根据URL进行证件识别", () -> xhCertService.recognizeCertificateByUrl(imageUrl, finalCertType));
    }

    /**
     * 获取证件图片数据接口（用于获取完整的图片base64数据）
     *
     * @param imageBase64 图片的base64编码数据
     * @param certType    证件类型
     * @param field       要获取的字段名，如"head_portrait"、"crop_image"等
     * @return 包含完整图片数据的响应
     */
    @PostMapping("/getImageData")
    public AjaxResult getImageData(@RequestParam("imageBase64") String imageBase64,
                                   @RequestParam("certType") String certType,
                                   @RequestParam("field") String field) {
        if (StringUtils.isEmpty(imageBase64)) {
            return AjaxResult.error("图片数据不能为空");
        }

        if (StringUtils.isEmpty(certType)) {
            return AjaxResult.error("证件类型不能为空");
        }

        if (StringUtils.isEmpty(field)) {
            return AjaxResult.error("字段名不能为空");
        }

        // 首先需要识别证件，获取完整的字段数据
        AjaxResult recognizeResult = validateTokenAndExecute("获取证件图片数据接口", () -> xhCertService.recognizeCertificate(imageBase64, certType));

        if (!recognizeResult.isSuccess()) {
            return recognizeResult;
        }

        // 根据不同证件类型，获取对应的infoKey
        String infoKey = getInfoKeyByCertType(certType);
        if (infoKey == null) {
            return AjaxResult.error("不支持的证件类型: " + certType);
        }

        // 从识别结果中提取字段数据
        Map<String, Object> infoMap = (Map<String, Object>) recognizeResult.get(infoKey);
        if (infoMap == null) {
            return AjaxResult.error("识别结果中未包含字段数据");
        }

        // 获取请求的字段数据
        Object fieldData = null;

        // 处理字段名的中文映射
        switch (field) {
            case "head_portrait":
                fieldData = infoMap.get("头像");
                break;
            case "crop_image":
                fieldData = infoMap.get("证件图像");
                break;
            case "id_number_image":
                fieldData = infoMap.get("身份证号图像");
                break;
            default:
                // 尝试直接获取字段
                fieldData = infoMap.get(field);
                // 如果获取不到，尝试获取中文映射字段
                if (fieldData == null) {
                    for (Map.Entry<String, Object> entry : infoMap.entrySet()) {
                        if (entry.getKey().contains(field)) {
                            fieldData = entry.getValue();
                            break;
                        }
                    }
                }
        }

        if (fieldData == null) {
            return AjaxResult.error("未找到请求的字段: " + field);
        }

        // 创建返回数据，只包含请求的字段
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put(field, fieldData);

        return AjaxResult.success("获取字段数据成功")
                .put("fieldData", resultMap);
    }

    /**
     * 根据证件类型获取对应的信息键
     *
     * @param certType 证件类型
     * @return 信息键名
     */
    private String getInfoKeyByCertType(String certType) {
        switch (certType) {
            case "id_card":
                return "idCardInfo";
            case "bank_card":
                return "bankCardInfo";
            case "business_license":
                return "licenseInfo";
            case "vehicle_license":
                return "vehicleLicenseInfo";
            case "driver_license":
                return "driverLicenseInfo";
            case "passport":
                return "passportInfo";
            case "social_security_card":
                return "socialSecurityCardInfo";
            case "household_register":
                return "householdRegisterInfo";
            case "marriage_certificate":
                return "marriageCertificateInfo";
            case "property_certificate":
                return "propertyCertificateInfo";
            case "birth_certificate":
                return "birthCertificateInfo";
            case "degree_certificate":
                return "degreeCertificateInfo";
            default:
                return null;
        }
    }
}
